using System;
using System.Collections.Generic;
using UnityEngine;

/// <summary>
/// 基于运行系统时间的时钟，可获得对应的时间，或者设置Timer
/// </summary>
class SystemTimer : MonoBehaviour
{
    //----------------------------------------------------------------------------
    //							时间获取
    //----------------------------------------------------------------------------
    /// <summary>
    /// 获得系统的时间，用long表示（0.00000001秒）
    /// </summary>
    /// <returns></returns>
    public static long GetSysTime()
    {
        return DateTime.UtcNow.ToFileTimeUtc();
    }

    /// <summary>
    /// 获得前后时间差（秒）
    /// </summary>
    public static float GetTimeSpanSeconds(DateTime timeBefore, DateTime timeAfter)
    {
        TimeSpan span = timeAfter - timeBefore;

        return (int)span.TotalSeconds;
    }

    /// <summary>
    /// 获得前后时间差（秒）
    /// </summary>
    /// <param name="timeAfter">如果无传入此值，或小于0，则取系统时间</param>
    public static float GetTimeSpanSeconds(long timeBefore, long timeAfter = -1)
    {
        //FileTimeUtc将生成的是1秒=10,000,000纳秒，以1601年为起始点计时；
        //(time1/3600/10000000/24/365)+1601)
        if (timeAfter <= 0)
        {
            timeAfter = GetSysTime();
        }
        double delta = ((double)timeAfter - timeBefore) / 10000000;
        return (float)delta;
    }


    /// <summary>
    /// 将float秒的时间向上取整，再转为00：00：00格式
    /// </summary>
    /// <param name="fValue">秒</param>
    /// <returns></returns>
    public static string TimeToStr(float fValue)
    {
        int value = (int)Mathf.Ceil(fValue);
        int hours = value / 3600;
        value = value % 3600;

        int min = value / 60;
        value = value % 60;

        int secs = value;

        return string.Format(
            "{0}:{1}:{2}",
            hours.ToString("00"),
            min.ToString("00"),
            secs.ToString("00")
            );
    }

    /// <summary>
    /// 将00:00:00的字符串转为int时间（秒）
    /// </summary>
    /// <returns>返回秒为单位的时间</returns>
    public static int StrToTime(string str)
    {
        string[] timeStrs = str.Split(':');

        int hours, mins, secs;

        int.TryParse(timeStrs[0], out hours);
        int.TryParse(timeStrs[1], out mins);
        int.TryParse(timeStrs[2], out secs);

        int result = hours * 3600 + mins * 60 + secs;
        return result;
    }

    //----------------------------------------------------------------------------
    //						计时器部分
    //----------------------------------------------------------------------------
    /// <summary>
    /// The delegater for coroutine
    /// </summary>
    static SystemTimer _instance;

    /// <summary>
    /// 所有定时器都停止与否
    /// </summary>
    static bool isAllTimmerStop = false;

    /// <summary>
    /// 所有的定时器，包括一个全局定时器
    /// </summary>
    /// <remarks>对于第0个Timer，将用作全局Timer，每个指定Tick会回调所有要监听全局时钟的方法</remarks>
    List<TimerInfo> m_timers = new List<TimerInfo>();

    /// <summary>
    /// 全局的Timer，可用于同步通知所有需要协同一个时间点的方法
    /// <para>如模拟每天时间流逝，则要用到GlobalTimer。</para>
    /// <remarks> 一般情况下，globalTimer视作普通TimerInfo，在Update中被调用</remarks>
    /// </summary>
    GlobalTimerInfo m_refGlobalTimer;

    /// <summary>
    /// 是否允许
    /// </summary>
    public static bool IsTimerStop { get { return isAllTimmerStop; } }

    /// <summary>
    /// 单例
    /// </summary>
    static SystemTimer g_instance
    {
        get
        {
            if (_instance == null)
            {
                _instance = new GameObject("CoroutineDelegater").AddComponent<SystemTimer>();

                //生成全局共用的Timer
                var glTimer = new GlobalTimerInfo()
                {
                    tickTockTime = 1,                       //-- 默认一秒Tick 一次
                    callOnce = false,                       //-- 一直Tick
                };

                _instance.m_refGlobalTimer = glTimer;
                _instance.m_timers.Add(glTimer); //GlobalTimer视作普通Timer传入Timers中
            }
            return _instance;
        }
    }

    /// <summary>
    /// 设置所有Timmer停止与否
    /// </summary>
    /// <param name="isStop">If set to <c>true</c> is stop.</param>
    public static void SetAllTimerStop(bool isStop)
    {
        isAllTimmerStop = isStop;
    }

    /// <summary>
    /// 设置一个延迟动作，（无需Mono类） Sets the action after.
    /// </summary>
    public static void SetDelayTimer(System.Action cbAction, float timeDelta)
    {
        GameObjFunc.AssetNotNull(cbAction);

        g_instance.m_timers.Add(new TimerInfo()
        {
            callbackFunc = cbAction,
            tickTockTime = timeDelta,
            timeLeft = timeDelta,
            callOnce = true,
        });
    }

    /// <summary>
    /// 设置全局的Timer的Tick时间
    /// </summary>
    /// <param name="tickTime"></param>
    public static void SetGlobalTimerTick(float tickTime)
    {
        g_instance.m_refGlobalTimer.tickTockTime = tickTime;
    }

    /// <summary>
    /// 增加全局Timer的回调
    /// </summary>
    /// <param name="funcKey"></param>
    /// <param name="callbackFunc"></param>
    /// <returns></returns>
    public static object AddOnGlobalTimerTick(object funcKey, System.Action callbackFunc)
    {
        g_instance.m_refGlobalTimer.AddCallback(funcKey, callbackFunc);

        return funcKey;
    }

    [System.Obsolete("DO NOT USE THIS FUNC without class instance, use AddOnGlobalTimerTick() instead", true)]
    public static void AddListenerOnGlobalTick(System.Action callbackFunc)
    {

        g_instance.m_refGlobalTimer.callbackFunc += callbackFunc;
    }

    /// <summary>
    /// 删掉某个在全局Timer上的回调
    /// </summary>
    /// <param name="funcKey"></param>
    public static void RemoveOnGlobalTimerTick(object funcKey)
    {

        g_instance.m_refGlobalTimer.RemoveCallback(funcKey);
    }

    /// <summary>
    /// 设置一个每间隔时间段更新的Timer
    /// </summary>
    /// <param name="cbUpdateAction">update action.</param>
    /// <param name="timeDelta">Time delta.</param>
    public static TimerInfo SetUpdateTimer(System.Action cbUpdateAction, float timeDelta)
    {
        GameObjFunc.AssetNotNull(cbUpdateAction);

        var timer = new TimerInfo()
        {
            callbackFunc = cbUpdateAction,
            tickTockTime = timeDelta,
            timeLeft = timeDelta,
            callOnce = false,
        };
        g_instance.m_timers.Add(timer);

        //创建完成后，返回这个Timer
        return timer;
    }

    /// <summary>
    /// 删除某个单一的UpdateTimer，并返回删除成功与否
    /// </summary>
    /// <param name="timer"></param>
    /// <returns></returns>
    public static bool RemoveUpdateTimer(TimerInfo timer)
    {
        return g_instance.m_timers.Remove(timer);
    }

    float timePassed = 0;
    const float eachUpdateTime = 0.25f;
    private void Update()
    {
        if (isAllTimmerStop)
        {
            return;
        }

        timePassed += Time.deltaTime;
        if (timePassed > eachUpdateTime)
        {
            TimerInfo timer = null;
            //尝试遍历所有需要的Timmer，并对到时间的进行回调：
            for (int i = 0; i < m_timers.Count; i++)
            {
                timer = m_timers[i];
                timer.timeLeft -= timePassed;
                if (timer.timeLeft < 0)
                {
                    if (timer.callbackFunc != null)
                    {
                        timer.callbackFunc();
                    }
                    if (timer.callOnce)
                    {
                        m_timers.RemoveAt(i);           //只调用一次的，已经完成使命，则直接删掉该位置的Timer
                        i--;
                    }
                    else
                    {
                        timer.timeLeft = timer.tickTockTime - (timePassed - eachUpdateTime);//Timer重新开始，但是由于帧时间可能超过了一点点，所以这里予以减去，补偿流掉的时间
                    }
                }
            }

            timePassed -= eachUpdateTime;
        }
    }

    /// <summary>
    /// 关于定时器的信息
    /// </summary>
    public class TimerInfo
    {
        /// <summary>
        /// 每次倒计时完成后的回调动作
        /// </summary>
        public System.Action callbackFunc;

        /// <summary>
        /// 是否完成后不再回调，将删除
        /// </summary>
        public bool callOnce;

        /// <summary>
        /// 倒计时时间
        /// </summary>
        public float tickTockTime;

        /// <summary>
        /// 还剩多少时间进行TickTock
        /// </summary>
        public float timeLeft;
    }

    /// <summary>
    /// 存储了多个TimerInfo，且可同时调用、可删除某个类实例对应的TimerInfo
    /// </summary>
    class GlobalTimerInfo : TimerInfo
    {
        /// <summary>
        /// 存储了多个TimerInfo，且可同时调用、可删除某个类实例对应的TimerInfo
        /// </summary>
        Dictionary<object, System.Action> m_cbsForObj = new Dictionary<object, Action>();

        /// <summary>
        /// 待添加的Callbacks：
        /// </summary>
        List<KeyValuePair<object, System.Action>> m_cbsToAdd = new List<KeyValuePair<object, Action>>();

        /// <summary>
        /// 待删除的Callback对应的Key值：
        /// </summary>
        List<object> m_cbsToRemove = new List<object>();

        /// <summary>
        /// .ctor：创建回调方法，调用需要统一回调的方法们
        /// </summary>
        public GlobalTimerInfo()
        {
            //其回调方法是无需修改的，直接创建为遍历所有存储的TimerInfo
            this.callbackFunc =
                () =>
                {

                    //待添加的回调方法：
                    if (m_cbsToAdd.Count > 0)
                    {
                        for (int i = 0; i < m_cbsToAdd.Count; i++)
                        {
                            m_cbsForObj.Add(m_cbsToAdd[i].Key, m_cbsToAdd[i].Value);
                        }

                        m_cbsToAdd.Clear();
                    }

                    //待删除的回调方法：
                    if (m_cbsToRemove.Count > 0)
                    {
                        for (int i = 0; i < m_cbsToRemove.Count; i++)
                        {
                            m_cbsForObj.Remove(m_cbsToRemove[i]);
                        }

                        m_cbsToRemove.Clear();
                    }

                    if (m_cbsForObj.Count > 0)
                    {
                        foreach (var cbFunc in m_cbsForObj.Values)
                        {
                            if (cbFunc != null)
                            {
                                cbFunc();
                            }
                        }

                    }
                };
        }

        /// <summary>
        /// 添加回调
        /// </summary>
        /// <param name="key">可指定哪个实例需要的回调，或另行传入唯一id，调用者自行确保其唯一性</param>
        /// <param name="callbackFunc">需要加入到全局Timer中的回调方法</param>
        public void AddCallback(object key, System.Action callbackFunc)
        {
            m_cbsToAdd.Add(new KeyValuePair<object, Action>(key, callbackFunc));
        }

        /// <summary>
        /// 删除回调
        /// </summary>
        /// <param name="key">可指定哪个实例需要的回调，或另行传入唯一id。需在Add中出现的</param>
        public void RemoveCallback(object key)
        {
            m_cbsToRemove.Add(key);
        }

    }
}

